#ifndef SamplerStateCollection_h
#define SamplerStateCollection_h

#include <Defines.h>
#include <CSharpPropertyMethods.h>

// Riccsson includes.
#include <System/Object.h>
#include <System/Type.h>
#include <System/String.h>

// XNA includes.
#include "States/SamplerState.h"

namespace Riccsson
{
	namespace Xna
	{
		namespace Framework
		{
			namespace Graphics
			{
				class SamplerStateCollection sealed
				{
					private: GC_PTR<Riccsson::System::Array<SamplerState>> _samplers;

			#if DIRECTX
					private: int _d3dDirty;
			#endif

					public: /*internal*/ SamplerStateCollection( int maxSamplers )
					{
						_samplers = new Riccsson::System::Array<SamplerState>(maxSamplers);

						for (var i = 0; i < maxSamplers; i++)
							//_samplers[i] = SamplerState::LinearWrap;
								throw; // TODO: Use Get/Set methods instead.
            
			#if DIRECTX
						_d3dDirty = int.MaxValue;
			#endif
					}
		
			//		public: SamplerState operator[](int index)
			//		{
			//			get 
			//			{ 
			//				return _samplers[index]; 
			//			}
			//
			//			set 
			//			{
			//				if (_samplers[index] == value)
			//					return;
			//
			//				_samplers[index] = value;
			//
			//#if DIRECTX
			//				_d3dDirty |= 1 << index;
			//#endif
			//			}
			//		}
							// TODO: Implement Set/Get methods instead. operator[] is not dynamic like C#.

					public: /*internal*/ void Clear()
					{
						for (var i = 0; i < _samplers->Length; i++)
							//_samplers[i] = null;
								throw; // TODO: Use Get/Set methods instead.
            
			#if DIRECTX
						_d3dDirty = int.MaxValue;
			#endif
					}

					/// <summary>
					/// Mark all the sampler slots as dirty.
					/// </summary>
					public: /*internal*/ void Dirty()
					{
			#if DIRECTX
						_d3dDirty = int.MaxValue;
			#endif
					}

					public: /*internal*/ void SetSamplers(GC_PTR<GraphicsDevice> device)
					{
			#if DIRECTX
						// Skip out if nothing has changed.
						if (_d3dDirty == 0)
							return;

						// NOTE: We make the assumption here that the caller has
						// locked the d3dContext for us to use.
						var pixelShaderStage = device._d3dContext.PixelShader;

						for (var i = 0; i < _samplers.Length; i++)
						{
							var mask = 1 << i;
							if ((_d3dDirty & mask) == 0)
								continue;

							var sampler = _samplers[i];
							SharpDX.Direct3D11.SamplerState state = null;
							if (sampler != null)
								state = sampler.GetState(device);

							pixelShaderStage.SetSampler(i, state);

							_d3dDirty &= ~mask;
							if (_d3dDirty == 0)
								break;
						}

						_d3dDirty = 0;

			#elif OPENGL

						for (var i = 0; i < _samplers.Length; i++)
						{
							var sampler = _samplers[i];
							var texture = device.Textures[i];

							if (sampler != null && texture != null && sampler != texture.glLastSamplerState)
							{
								// TODO: Avoid doing this redundantly (see TextureCollection.SetTextures())
								// However, I suspect that rendering from the same texture with different sampling modes
								// is a relatively rare occurrence...
								GL.ActiveTexture(TextureUnit.Texture0 + i);
								GraphicsExtensions.CheckGLError();

								// NOTE: We don't have to bind the texture here because it is already bound in
								// TextureCollection.SetTextures(). This, of course, assumes that SetTextures() is called
								// before this method is called. If that ever changes this code will misbehave.
								// GL.BindTexture(texture.glTarget, texture.glTexture);
								// GraphicsExtensions.CheckGLError();

								sampler.Activate(texture.glTarget, texture.LevelCount > 1);
								texture.glLastSamplerState = sampler;
							}
						}
			#endif
					}
				};
			}
		}
	}
}

#endif
